#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include "my_libproc.h"

long get_cpu_num()
{
    return sysconf(_SC_NPROCESSORS_CONF);
}

Cpu_Time* init_cpu_time(Cpu_Time** cpu_time)
{
    long cpu_num = get_cpu_num();
    *cpu_time = malloc(sizeof(Cpu_Time) + (cpu_num * sizeof(cpu_time_t)));
    if (cpu_time == NULL)
    {
        perror("malloc cpu_time failed");
        return NULL;
    }
    (*cpu_time)->cpu_num = cpu_num;
    return *cpu_time;
}

void free_cpu_time(Cpu_Time* cpu_time)
{
    free(cpu_time);
}

int get_cpu_time(Cpu_Time* cpu_time)
{
    struct timespec record_timestamp;
    clock_gettime(CLOCK_MONOTONIC, &record_timestamp);
    long cpu_num = cpu_time->cpu_num;
    const char proc_stat[32] = "/proc/stat";

    FILE* fp = fopen(proc_stat, "r");
    if (fp == NULL)
    {
        perror("fopen proc_stat failed");
        return errno;
    }
    char line[256];
    int ret;
    cpu_time_t cpu_time_it;
    // 第一行读取cpu all
    if (fgets(line, sizeof(line), fp) != NULL)
    {
        ret = sscanf(line, "%*s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
            &cpu_time_it.user_time,
            &cpu_time_it.nice_time,
            &cpu_time_it.system_time,
            &cpu_time_it.idle_time,
            &cpu_time_it.iowait_time,
            &cpu_time_it.irq_time,
            &cpu_time_it.softirq_time,
            &cpu_time_it.steal_time,
            &cpu_time_it.guest_time,
            &cpu_time_it.guest_nice_time
        );
        if (ret > 0)
        {
            cpu_time_it.used_time = cpu_time_it.user_time + cpu_time_it.nice_time + cpu_time_it.system_time + cpu_time_it.irq_time
                + cpu_time_it.iowait_time + cpu_time_it.softirq_time + cpu_time_it.steal_time + cpu_time_it.guest_time + cpu_time_it.guest_nice_time;
            cpu_time_it.total_time = cpu_time_it.used_time + cpu_time_it.idle_time;
            cpu_time_it.timestamp = record_timestamp.tv_sec + record_timestamp.tv_nsec / 1000;
            cpu_time->cpu_all = cpu_time_it;
        }
    }

    long i = 0;
    while (i != cpu_num)
    {
        if (fgets(line, sizeof(line), fp) == NULL) {
            break;
        }
        if (strstr(line, "cpu"))
        {
            ret = sscanf(line, "%*s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
                &cpu_time_it.user_time,
                &cpu_time_it.nice_time,
                &cpu_time_it.system_time,
                &cpu_time_it.idle_time,
                &cpu_time_it.iowait_time,
                &cpu_time_it.irq_time,
                &cpu_time_it.softirq_time,
                &cpu_time_it.steal_time,
                &cpu_time_it.guest_time,
                &cpu_time_it.guest_nice_time
            );
            if (ret > 0)
            {
                cpu_time_it.used_time = cpu_time_it.user_time + cpu_time_it.nice_time + cpu_time_it.system_time + cpu_time_it.irq_time
                    + cpu_time_it.iowait_time + cpu_time_it.softirq_time + cpu_time_it.steal_time + cpu_time_it.guest_time + cpu_time_it.guest_nice_time;
                cpu_time_it.total_time = cpu_time_it.used_time + cpu_time_it.idle_time;
                cpu_time_it.timestamp = record_timestamp.tv_sec + record_timestamp.tv_nsec / 1000;
                cpu_time->cpus[i] = cpu_time_it;
            }
        }
        i++;
    }
    fclose(fp);
    return 0;
}

void show_cpu_time(Cpu_Time* cpu_time)
{
    printf("cpu %-8s %-8s  %-8s  %-8s  %-8s  %-8s  %-8s  %-8s  %-8s  %-8s\n",
        "user", "nice", "system", "idle", "iowait", "irq", "softirq", "steal", "guest", "guest_nice");
    cpu_time_t* cpu_ptr;
    cpu_ptr = &cpu_time->cpu_all;
    printf("cpu all  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld\n",
        cpu_ptr->user_time,
        cpu_ptr->nice_time,
        cpu_ptr->system_time,
        cpu_ptr->idle_time,
        cpu_ptr->iowait_time,
        cpu_ptr->irq_time,
        cpu_ptr->softirq_time,
        cpu_ptr->steal_time,
        cpu_ptr->guest_time,
        cpu_ptr->guest_nice_time
    );
    long cpu_num = cpu_time->cpu_num;
    for (long i = 0; i < cpu_num; i++)
    {
        cpu_ptr = &cpu_time->cpus[i];
        printf("cpu[%ld]  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld  %-8ld\n",
            i,
            cpu_ptr->user_time,
            cpu_ptr->nice_time,
            cpu_ptr->system_time,
            cpu_ptr->idle_time,
            cpu_ptr->iowait_time,
            cpu_ptr->irq_time,
            cpu_ptr->softirq_time,
            cpu_ptr->steal_time,
            cpu_ptr->guest_time,
            cpu_ptr->guest_nice_time
        );
    }
}

int cal_cpu_usage(Cpu_Time* last_cpu_time, Cpu_Time* now_cpu_time)
{
    if (last_cpu_time == NULL || now_cpu_time == NULL)
    {
        return -1;
    }

    cpu_time_t* last_ptr, * now_ptr;
    // 总cpu使用率
    last_ptr = &last_cpu_time->cpu_all;
    now_ptr = &now_cpu_time->cpu_all;
    now_ptr->usage = (now_ptr->used_time - last_ptr->used_time) / (now_ptr->total_time - last_ptr->total_time);

    // 单cpu使用率
    for (long i = 0; i < now_cpu_time->cpu_num; i++)
    {
        last_ptr = &last_cpu_time->cpus[i];
        now_ptr = &now_cpu_time->cpus[i];
        now_ptr->usage = (now_ptr->used_time - last_ptr->used_time) / (now_ptr->total_time - last_ptr->total_time);
    }

    return 0;
}

void show_cpu_usage(Cpu_Time* now_cpu_time)
{
    printf("cpu_all: %.2f%%\n", now_cpu_time->cpu_all.usage);
    cpu_time_t* now_ptr;
    // 单cpu使用率
    for (long i = 0; i < now_cpu_time->cpu_num; i++)
    {
        now_ptr = &now_cpu_time->cpus[i];
        printf("cpu[%2ld]: %.2f%%\n", i, now_ptr->usage);
    }
    printf("--------------------------------------\n");
}
